終於來到 Collection Types 三部曲最終章:Dictionary ,什麼是 Dictionary 呢?
我們先來看一段 API Response:
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!",
"body": "The shortest article. Ever.",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
},
"relationships": {
"author": {
"data": {"id": "42", "type": "people"}
}
}
}]
}
可以發現在大括號中,有一對一對的資料,就好比 "type": "articles"
、"id": "1"
或是 "data": {"id": "42", "type": "people"}
,這種沒有一定順序的 Key: Value 組合資料,就是 Dictionary,常被用來處理 API 回傳的資料。
Dictionary 的宣告方式其實跟其他兩個集合型別也是很相似,Dictionary 宣告型別時,完整的寫法是這樣:
Dictionary<KeyType, ValueType>
所以假定今天有一個字串的 key,對上整數的 Value,你就可以這樣寫:
Dictionary<String, Int>
但是你也可以使用縮寫就會變成這樣:
[String: Int]
那麼以下的宣告方式都是相同的效果~
var 字典1: Dictionary<String, Int> = [:]
var 字典2 = Dictionary<String, Int>()
var 字典3: [String: Int] = [:]
var 字典4 = [String: Int]()
注意到這邊在給定初始值為空字典時,使用的是 [:]
。
透過宣告可以了解,這個 Dictionary 是被包在 []
內,並且由一對一對的 String: Int
所組合而成的。
var 大兵日記: [String: String] = [:]
大兵日記 = ["日期": "2019-12-31", "天氣": "陰天", "心情": "爛透了"]
來看看上面的範例,在大兵日記這個 Dictionary,他是一個 [String: String],鍵跟值都是 String 型別,所以可以看到裡面有日期
、天氣
以及心情
對應到相對的值,而這三組資料每個都以 ,
做區隔。
理所當然的,也可以不用特別註記型別就可以透過型別推論出型別喔,但是只局限於字典裡每組資料型別都是相同的( [String, String] 或是 [Int: String] ... )。
let countryCode = ["TW": "Taiwan", "JP": "Japan", "US": "United States"]
print("The Type of Country Code is \(type(of: countryCode))")
// The Type of Country Code is Dictionary<String, String>
那為什麼會叫做 Dictionary 呢?其實這名稱其來有自,在取用 Dictionary 時,我們可以直接依照鍵來尋找值,就像查字典一樣,我們繼續以上面的 countryCode
來解釋:
print(countryCode["TW"])
// Optional("Taiwan")
針對 countryCode 使用 [key]
的方式來查詢,但是你有沒有發現,找出來的值為什麼不是 Taiwan
而是 Optional("Taiwan")
?
還記得在基礎介紹時提到,Swift 其實對於程式碼安全性是很講究的,因為你並不能保證輸入的 Key 都一定能找到相對應的 Value,像是下面的範例,在字典中並沒有 "GB"
,所以在取用的時候會找不到值,也因為 Swift 程式碼安全,會回傳 nil,所以如果真的要針對取出來的值做進一步處理,最好的方式就是確保這個 Key 取出來是真的有值。
print(countryCode["GB"])
// nil
if let countryName = countryCode["TW"] {
print("\(countryName) NO.1")
}
至於 count
以及 isEmpty
這兩個屬性,也是可以在 Dictionary 使用喔。
print(countryCode.count) // 3
print(countryCode.isEmpty) // false
那如果今天想修改 Dictionary 裡面的值該如何做呢?還記得上面的取用方式嗎?
Dictionary[Key] = Value
找到值,並且指派新的值給它,這樣就可以修改值囉!
var 小胖便當價格表 = [
"A餐": 100,
"B餐": 200,
"C餐": 300
]
小胖便當價格表["C餐"] = 400
print(小胖便當價格表["C餐"]!) // 400
除了可以修改既有的資料外,也可以新增資料,一樣是透過上述的方法來新增:
小胖便當價格表["D餐"] = 500
print(小胖便當價格表["D餐"]!)
// ["C餐": 400, "A餐": 100, "B餐": 200, "D餐": 500]
// 新增了 D餐: 500
// 因為 Dictionary 是跟 Set 一樣是無序排列
// 每一次印出來的順序也都不一樣喔 :)
除了使用這種方法,Dictionary 也有一個實體方法也可以新增資料:
updateValue(_:forKey:)
以上面修改及新增 C、D 餐的寫法,換成 updateValue,寫法就會像是這樣:
小胖便當價格表.updateValue(400, forKey:"C餐")
小胖便當價格表.updateValue(500, forKey:"D餐")
但是這上面都是在說如何修改 Value,但是可以修改 Key 嗎?
原則上不太建議,因為 Dictionary 會檢查 Key 是否實作 Hashable Protocol ,確保同一個 Dictionary 中,不會有重複的 Key,任意更改的結果可能就會同個 Dictionary 有兩組相同的 Key ,就會造成錯誤。
但是你真的要改的話也不是不行~
原則上建議先找到舊的 Key-Value,移除掉後再新增新的 Key,但是要怎麼移除資料呢?
removeValue(forKey:)
:就像取用的方式,依照 Key 來找尋,刪除var exampleDictionary = [1: "A", 2: "B", 3: "C"]
exampleDictionary.removeValue(forKey: 3)
print(exampleDictionary) // [1: "A", 2: "B"]
exampleDictionary[4] = "C"
print(exampleDictionary) // [1: "A", 2: "B", 4: "C"]
這時候 3
就被替換成 4
了,也不會因為重複造成錯誤。
Dictionary 實作了 Collection Protocol,理所當然地可以使用 for...in loop
走訪每組資料,但這邊就有些許的差異,如果你是想走訪每組資料,那就像先前一樣:
var exampleDictionary = [1: "A", 2: "B", 3: "C"]
for data in exampleDictionary {
print(data)
}
/*
(key: 3, value: "C")
(key: 1, value: "A")
(key: 2, value: "B")
*/
但是你可以發現印出來的型別是一個 Tuple
,所以我們也可以使用 Tuple 來去接收 Key 以及 Value:
for (dataKey, dataValue) in exampleDictionary {
print("Data Key = \(dataKey)")
print("Data Value = \(dataValue)")
}
/*
Data Key = 1
Data Value = A
Data Key = 2
Data Value = B
Data Key = 3
Data Value = C
*/
假定你想紀錄字典裡所有的 Keys 或是 Values,你可以使用 Dictionary.keys
以及 Dictionary.values
,他會回傳整個 keys 或是 values,你可以宣告一個 Array 中來存放。
let dataKeys = [Int](exampleDictionary.keys)
// [1, 2, 3]
let dataValues = [String](exampleDictionary.values)
// ["A", "B", "C"]